package com.butcher.framework.kernel.object.serialize;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.butcher.framework.kernel.constants.ErrorCode;
import com.butcher.framework.kernel.constants.FastHttpConstants;
import com.butcher.framework.kernel.endpoint.http.transform.object.ObjectTool;
import com.butcher.framework.kernel.endpoint.http.type.TypeFactory;
import com.butcher.framework.kernel.endpoint.http.type.WrapType;
import com.butcher.framework.kernel.exc.FastHttpException;

public class SerializeUtil {
	public static Object readFields(DataInput in) throws FastHttpException {
		Object object = null;
		try {
			// 参数为空
			if (FastHttpConstants.NULL_PARAM == in.readByte()) {
				return null;
			}
			byte tc = in.readByte();
			String className;
			switch (tc) {
			case FastHttpConstants.TC_ARRAY:
				object = readArray(in);
				break;
			case FastHttpConstants.TC_LIST:
				object = readList(in);
				break;
			case FastHttpConstants.TC_MAP:
				object = readMap(in);
				break;
			case FastHttpConstants.TC_OBJECT:
				className = in.readUTF();
				Object obj = Class.forName(className).newInstance();
				object = ObjectTool.getOBJChain().doRead(in, obj.getClass(),
						obj.getClass());
				break;
			case FastHttpConstants.TC_PRIMITIVE:

				className = in.readUTF();
				WrapType type = TypeFactory.getInstance().getType(className);
				object = type.doRead(in);
				break;
			}
		} catch (Exception e) {
			throw new FastHttpException(ErrorCode.ERROR_OBJECT_TRANSFORM, e);
		}
		return object;
	}

	public static void write(DataOutput out, Object object)
			throws FastHttpException {
		// 需要序列化的对象为空，写一个标识位出去即可

		try {
			if (null == object) {
				out.writeByte(FastHttpConstants.NULL_PARAM);
				return;
			}
			out.writeByte(FastHttpConstants.NOT_NULL_PARAM);
			if (object.getClass().isArray()) {
				writeArray(out, object);
			} else if (isListOrMap(object.getClass(), "java.util.List",
					"java.util.ArrayList")) {
				writeList(out, object);
			} else if (isListOrMap(object.getClass(), "java.util.Map",
					"java.util.HashMap")) {
				writeMap(out, object);

			} else if (isBasicType(object.getClass(), "java.lang")) {
				out.writeByte(FastHttpConstants.TC_PRIMITIVE);
				out.writeUTF(object.getClass().getName());
				ObjectTool.getOBJChain().doWrite(out, object);
			} else {
				out.writeByte(FastHttpConstants.TC_OBJECT);
				out.writeUTF(object.getClass().getName());
				ObjectTool.getOBJChain().doWrite(out, object);
			}
		} catch (Exception e) {
			throw new FastHttpException(ErrorCode.ERROR_OBJECT_TRANSFORM, e);
		}
	}

	/**
	 * 序列化方法
	 * 
	 * @param object
	 * @return
	 * @throws IOException
	 */
	public static byte[] serialize(Object object) throws IOException {
		ObjectOutputStream oos = null;
		ByteArrayOutputStream baos = null;

		baos = new ByteArrayOutputStream();
		oos = new ObjectOutputStream(baos);
		oos.writeObject(object);
		byte[] bytes = baos.toByteArray();
		return bytes;

	}

	/**
	 * 反序列化
	 * 
	 * @param bytes
	 * @return
	 * @throws IOException
	 * @throws ClassNotFoundException
	 */
	public static Object unserialize(byte[] bytes) throws IOException,
			ClassNotFoundException {
		ByteArrayInputStream bais = null;

		bais = new ByteArrayInputStream(bytes);
		ObjectInputStream ois = new ObjectInputStream(bais);
		return ois.readObject();

	}

	protected static boolean isListOrMap(Class<?> clz, String intfPkg,
			String implPkg) {
		return (clz.getName().equals(intfPkg))
				|| (clz.getName().equals(implPkg));
	}

	protected static boolean isBasicType(Class<?> clz, String pkgName) {
		return (clz.isPrimitive())
				|| ((clz.getPackage() != null) && (clz.getPackage().getName()
						.equals(pkgName)));
	}

	private static void writeArray(DataOutput out, Object object)
			throws FastHttpException {
		try {
			out.writeByte(FastHttpConstants.TC_ARRAY);
			Object[] arr = (Object[]) object;
			int len = arr.length;
			out.writeInt(len);
			for (int i = 0; i < len; i++) {
				write(out, arr[i]);
			}
		} catch (IOException e) {
			throw new FastHttpException(ErrorCode.ERROR_OBJECT_TRANSFORM, e);
		}
	}

	private static Object readArray(DataInput in) throws FastHttpException {
		try {
			Object[] arr = new Object[in.readInt()];
			for (int i = 0; i < arr.length; i++) {
				arr[i] = readFields(in);
			}
			return arr;
		} catch (IOException e) {
			throw new FastHttpException(ErrorCode.ERROR_OBJECT_TRANSFORM, e);
		}

	}

	@SuppressWarnings("unchecked")
	private static void writeList(DataOutput out, Object object)
			throws FastHttpException {
		try {
			out.writeByte(FastHttpConstants.TC_LIST);
			List<Object> list = (List<Object>) object;
			int len = list.size();
			out.writeInt(len);
			for (int i = 0; i < len; i++) {
				write(out, list.get(i));
			}
		} catch (IOException e) {
			throw new FastHttpException(ErrorCode.ERROR_OBJECT_TRANSFORM, e);
		}
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static Object readList(DataInput in) throws FastHttpException {
		List list = new ArrayList();
		try {
			int size = in.readInt();
			for (int i = 0; i < size; i++) {
				list.add(readFields(in));
			}
			return list;
		} catch (IOException e) {
			throw new FastHttpException(ErrorCode.ERROR_OBJECT_TRANSFORM, e);
		}
	}

	@SuppressWarnings("unchecked")
	private static void writeMap(DataOutput out, Object object)
			throws FastHttpException {
		try {
			out.writeByte(FastHttpConstants.TC_MAP);
			Map<Object, Object> map = (Map<Object, Object>) object;
			int size = map.size();
			out.writeInt(size);
			Entry<Object, Object> en = null;
			for (Iterator<Entry<Object, Object>> it = map.entrySet().iterator(); it
					.hasNext();) {
				en = it.next();
				write(out, en.getKey());
				write(out, en.getValue());
			}
		} catch (IOException e) {
			throw new FastHttpException(ErrorCode.ERROR_OBJECT_TRANSFORM, e);
		}
	}

	private static Object readMap(DataInput in) throws FastHttpException {
		Map<Object, Object> map = new HashMap<Object, Object>();
		try {
			int size = in.readInt();
			for (int i = 0; i < size; i++) {
				map.put(readFields(in), readFields(in));
			}
			return map;
		} catch (IOException e) {
			throw new FastHttpException(ErrorCode.ERROR_OBJECT_TRANSFORM, e);
		}
	}
}
